查看原文
其他

回顾·Procedure V2介绍

张铎 DataFunTalk 2021-03-01


DataFun社区

大数据、算法的交流学习平台 圈内人都在关注

 

本文根据小米人工智能与云平台研发工程师张铎老师在中国HBase技术社区第二届MeetUp之“HBase技术解析及应用实践”中分享的《Procedure V2介绍》编辑整理而成,在未改变原意的基础上稍做修改。

张铎老师

今天分享的内容与HBase实践联系很密切,Procedure V2主要解决AssignmentManager问题在使用HBase时有时会遇到region在RAT状态中无法恢复很多时候只能将Master重启原因是原先AssignmentManager实现比较困难它的状态会存在在很多位置很难将所有的状态都保存为完全一致

Procedure V2架构很简单分为三大块Procedure Executor是主要的入口Procedure Scheduler就是提交到Executor的任务不会直接push到Procedure Scheduler里不同的队列,分优先级让优先级高的先运行Procedure Store也很重要确保状态保持一致如何恢复进入的任何一个Procedure先进行log,记录下来还能保证能取出来,类似write ahead log。就是进入先Insert,然后push到Procedure Scheduler跑完一步都会update直到最后完全执行完delete

Procedure Executor模块是submit进来会有一个Map存储所有Procedure的id还有就是将其push到Procedure Scheduler里Procedure Scheduler里有很多Worker默认是16个。将所有worker运行后结果存入CompletedTimeout Executor用于处理有些Worker不能处理的内容在过一段时间继续执行例如过5秒将任务塞回去,其他Worker继续执行,还有些就是定时任务,比如Completed不再需要的东西。因为有些任务是completed发请求,会过一段时查看是否执行完,如果已经被查询就不再需要了。

Worker执行流程为:首先是从scheduler中取出一个procedure(Poll a procedure from scheduler),接着Acquire lock,后续会讲解,然后去跑procedure(Execute the procedure),跑完就需要Update procedure Store,如果出现问题整个HMaster就会崩溃,如果重启所有任务重新运行,如果update成功就接着后续任务跑。然后依据运行完return value执行不同工作,如果返回的值是自己,需要保存自己的状态。跳回第三步接着运行;如果返回的是一些新的procedure list,将其记录为sub procedure,当前procedure休眠等待子procedure跑完恢复执行;如果返回的是null,则procedure已经跑完,可以删除该procedure。由于存在子procedure需要判断当前procedure是否有parent procedure,如果存在,在里面会有一个计数器,例如有10个subprocedure,跑完一个计数减一,当值为0,parent procedure就会继续执行。最后还需要将不需要的lock释放掉,就这就是Worker的大致流程。

接下来讲一下Procedure Lock的作用。其最主要的作用就是保证procedure执行时不被打断,它本身是一个读写锁,可以从parent procedure继承。Procedure有一个holdlock属性,将其设为true时,除非返回null否则release lock不会真正释放锁;为false(默认)则在被suspend时会释放锁,下次执行重新拿锁。当Assign Procedure/Unassign Procedure时该属性为true,需要拿着namespace和table的读锁,以及对应region的写锁,因为操作的时候你不能修改。还有就是Modify Table Procedure它的作用就是如在table上修改属性需要需要拿着namespace的读锁,以及对应table的写锁,但是holdlock值是false。其实Modify Table Procedure也不应该一直是false,前面好几步都是在改table属性,这段时间是不能被打断的。其原因是Assign Procedure/Unassign Procedure,需要拿着namespace和table的读锁,如果Modify Table Procedure的holdLock属性为true,需要拿着namespace的读锁,以及对应table的写锁,这样Assign Procedure/Unassign Procedure是无法执行的。后续设计需要对holdlock与procedure本身的状态有关系,有些状态跑完一步不能释放,但是到后续状态需要释放,在改table不能打断,最后需要将所有region都reopen将所有属性都load一遍时再释放锁。目前虽然还会有问题但是不经常遇见,不重启基本没事,运行只要不停返回自己就不会release lock,另一方面就算重启也是按照procedure的id顺序去执行,大部分时候不会被打乱。

Procedure Scheduler主要就是一个实现其他实现主要是写UT时测试一下逻辑真正在代码中要用的只有MasterProcedureScheduler它有几个概念首先是Queue里面元素就是Procedure针对相同目标的Procedure会放到一个Queue比如针对一个table的modifydeleteQueue没有region类型只有tableQueue同一个table的region操作也会放到一个Queue里面还有一个是RunQueue(代码中叫FairQueue),元素就是Queue,只有在Run Queue中的Queue中的procedure才能被Procedure Executor取出并执行。Procedure在拿到写锁时会把Queue从Run Queue中拿掉,释放时再放回。四种类型的Queue: Server Queue,Table Queue, Peer Queue,Meta Queue(Deprecated),procedure要么拿Queue上面的读锁要么拿上面的写锁,拿到写锁其他就不会在执行,把Queue从Run Queue中拿掉,减轻负担。RunQueue是很多Queue串在一起挨个拿当发现自己无法再执行就自己去掉

ProcedureStore代码比较复杂,WALProcedure Store, ProcedureStoreTracker这两个类是理解它最关键的地方。会有WAL但是不是LSM Tree的模式,所有procedure都会放进内存,启动时把所有WAL都replay一遍来恢复数据。如何避免跑的时间越来越长,WAL越来越多,重启速度越来越慢呢?过期的WAL会直接删除,procedure不像HBase中写进来不能丢,执行完就会删掉,因此大部分数据不会一直留在内存里面,会通过一个Tracker来维护活跃的Procedure的ID,会有一个范围,如果你WAL的最大ID比记录的最小ID小,就会删除。

接下来讲一下Procedure V2如何重新实现AssignmentManager V2,防止其不出bug。首先是不使用ZK,以前集群用ZK,整集群重启的时候ZK压力会很大。还有就是Procedure Lock保护,可以防止double assign问题,以前会出现region在这个Master上显示,在其他region server上也存在,regionserver有时会突然崩溃可能就是这个问题。Procedure完成之后状态一定是一致的(解决之前zk,AM和RS状态不一致的问题),可以将其当成一个是个事务,发生错误会重新开始跑。

Region状态迁移分为四个状态,还有个offline状态是为了兼容以前版本,Closed,Opening,Opened,Closing,创建一个region其实是在Closed状态,然后进入Opening,Opened,如果你要删除一个状态就进入Closing,Closed,之后就可以删除。主要分为AssignProcedure(Closed-Opening-Opened)和UNassignProcedure(Opened-Closing-Closed),这两个Procedure都是Region Transition Procedure的子类,无法回滚,会一直重试到执行完成。有两个异常,AssignProcedure会进入FAlLED_OPEN状态,如配置jre路径错误,coprocessor无法load,无法初始化,因此会设置一个最大尝试次数,超过就会将其放入FAlLED_OPEN,需要手动恢复。还有就是Region Server crash,后续会讲到。会什么会出现FAlLED_OPEN呢,就是region告诉无法打开,但是如果你调用超时,或者RS没有告诉你调用失败在,这种情况无法假定执行失败,只能不停重试直到RS告诉成功或失败,否则就会出现你认为调用失败你换了一个RS结果原来的RS实际执行成功了,就会出现double assign的问题。分布式系统RPC调用重试逻辑要非常注意Region Server crash是从更外部判断region确实是死了,这种情况才能打断。还有一个MoveRegionProcedure实际上比较复杂其实逻辑比较简单就是先Unassign,再Assign。

Server Crash Procedure分为两段先split_log然后assign先看有没有meta region如有有的话先进行split_meta_log,因为meta region的wal是单独写到一个文件然后assign_mataget_regions然后split_log然后再将所有region都assign一遍这样的原因是assign和unassign都需要在meta中更改状态如果meta不是online状态跑这些就毫无意义

HandleRIT就是处理RS crash,RIT是Region In Transition的意思。SCP需要打断仍然在重试的Assign Procdure/Unassign Procedure,因为RS挂掉的话重试也不会再成功而AM会为每个region保存当前正在执行的Region Transition Procedure,每个region最多只能有一个。如果正在跑Unassign Procedure直接退出因为Unassign Procedure就是将其转化为Closed状态现在RS挂了直接就进入CLOSED状态了然后分配一个Assign Procedure如果是Assign Procedure将状态退回初始状态重新开始

MoveRegionProcedure不是Region Transition Procedure的子类,HandleRIT时会通过AM去找,是不可能找到MRP的,因此无法打断MRP执行逻辑是先schedule一个UnassignProcedure再schedule一个AssignProcedure打断AssignProcedure没有问题打断UnassignProcedure之后,MRP不会释放锁,会直接schedule一个AssignProcedure。因此HandleRIT时机很重要,必须在split log完成之后才能打断,否则可能会丢数据。Merge/Split在Unassign时也有可能被打断,Unassign Procedure退出时,region可能遗留了部分recovered.edits,Merge/Split在link HFile时并不会对recovered.edits做处理,也会导致丢数据。因此改动就是在Unassign之后检查region是否有recovered.edits,如果有则让Merge/Split procedure失败回滚。

接下来讲一下在2.1版本Master启动流程也会有些改动,原来启动流程是Assignment Manager需要扫描meta恢复region状态,会有一个RecoverMetaProcedure在master启动时先跑这个procedure确保meta都online再执行后续的启动流程但在执行完成RMP之后,meta所在的RS依然有可能挂掉,因为你不能确保执行RMP之后meta一定online因此做的改动就是直接去掉这些东西,通过SCP来确保meta最终能够online,这种方式存在一个问题就是通过SCP来确保meta最终能够online需要知道哪些RS crash了,才能创建对应的SCP。HBASE的zk上注册着的RS是活着的,原来的办法是AM扫描meta可以得到有region的RS列表,两者一对比就可以确定哪些region死了。但是SCP来确保meta最终能够online来帮它扫,要schedule这个scp需要先让其扫描完,就会形成死锁,解决方案:通过扫描WAL目录来获得RS列表,再和zk上的列表比对。

新的启动流程会先初始化Procedure Executor,然后load procedures,再将其放到schedule里面,但是不启动worker。首先要判断meta region是否存在,但是如果是一个新的region server或者HBASE cluster,什么都没有,所以需要判断是否存在一个meta region,如果不存在的话schedule InitMetaProcedure。Meta region的TableDescriptor是在代码中写死,指定有几个family,所以只需要让meta在一个region serveronline这个工作就完成了,InitMetaProcedure就是schedule一个assign procedure然后online。然后扫描WAL目录获得RS列表,和zk上的列表进行比对,schedule SCP(先检查对应的RS是否已经有SCP),这些工作完成后再启动Procedure Executor的Worker,接着Assignment Manager开始尝试load meta,只要没有漏掉SCP,总是可以保证meta最终会online。

后续改进点就是Procedure Lock很重要防止运行时被打断但是重启时Procedure Lock不会恢复Procedure Store没有记录是否lock,因此重启会再拿一遍lock。比如两个modify procedure,先跑的A,A拿到lock,B没拿到,A执行到一半,但是重启两个都没lock,但是如果B先拿到lock就会出现问题。因此需要记录lock信息,重启时恢复Procedure Lock。统一Assign Procedure/Unassign Procedure/Move Region Procedure,这样HandleRIT能够统一处理,存入AM,能够简化逻辑。

——END

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存